package com.qd.panda.service.transfer;

import cn.hutool.core.math.Money;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.db.BaseService;
import com.cdqidi.dto.CacheKey;
import com.cdqidi.excel.ExcelExport;
import com.cdqidi.excel.util.ExportPage;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.RedisTemplateUtil;
import com.cdqidi.util.SpringContextHolder;
import com.qd.common.panda.domain.entity.shop.order.ProductOrderDTO;
import com.qd.common.panda.domain.entity.transfer.TransferDTO;
import com.qd.common.panda.domain.entity.transfer.UserDividedDTO;
import com.qd.common.panda.domain.entity.transfer.UserToBeTransferDTO;
import com.qd.common.panda.domain.entity.transfer.UserTransferDTO;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.panda.mapper.transfer.UserTransferMapper;
import com.qd.panda.model.ParkSpace;
import com.qd.panda.model.UserBank;
import com.qd.panda.model.UserTransfer;
import com.qd.panda.model.UserTransferRelation;
import com.qd.panda.service.space.ParkSpaceService;
import com.qd.panda.service.user.UserBankService;
import com.qd.system.service.login.LoginUserService;
import com.qd.system.service.org.OrgService;
import com.qd.system.service.user.BaseUserInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

import static java.util.stream.Collectors.toList;

/**
 * 转账信息
 *
 * @author sjk
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class UserTransferService extends BaseService<UserTransferMapper, UserTransfer> {

    private static final String PREFIX_ID = "panda_user_transfer_tid_{0}";

    private final RedisTemplateUtil<UserTransfer> redisTemplateUtil;
    private final UserBankService userBankService;
    private final ParkSpaceService parkSpaceService;
    private final UserTransferRelationService userTransferRelationService;
    @Resource
    private HttpServletResponse response;

    /**
     * 保存
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public UserTransferDTO save(UserTransferDTO dto) {
        validator(dto);
        final UserTransfer model = dtoToModel(dto);
        try {
            if (super.save(model)) {
                return modelToDto(model);
            }
            throw new ApiException("添加失败");
        } finally {
            clearCache(model);
            model.freeData();
        }
    }

    /**
     * 修改
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public UserTransferDTO update(UserTransferDTO dto) {
        validator(dto);
        final UserTransfer historyModel = Optional.ofNullable(getById(dto.getTid())).orElseThrow(() -> new ApiException("ID不存在,不能更新"));
        final UserTransfer model = dtoToModel(dto);
        try {
            if (super.updateById(model)) {
                return modelToDto(model);
            }
            throw new ApiException("更新失败");
        } finally {
            clearCache(historyModel);
            historyModel.freeData();
            model.freeData();
        }
    }

    /**
     * 批量添加
     *
     * @param mList 数据库对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveBatch(List<UserTransfer> mList) {
        try {
            if (!super.saveBatch(mList)) {
                throw new ApiException("批量添加失败");
            }
        } finally {
            clearCaches(mList);
        }
    }

    /**
     * 批量更新
     *
     * @param mList 数据库对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateBatchIds(List<UserTransfer> mList) {
        final List<String> ids = mList.stream().map(UserTransfer::getTid).collect(toList());
        final List<UserTransfer> historyModelList = checkIds(ids);
        try {
            if (!super.updateBatchById(mList)) {
                throw new ApiException("批量更新失败");
            }
        } finally {
            clearCaches(historyModelList);
            historyModelList.clear();
            ids.clear();
        }
    }

    /**
     * 删除，单个
     *
     * @param tid 主键
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeById(String tid) {
        final UserTransfer historyModel = Optional.ofNullable(getById(tid)).orElseThrow(() -> new ApiException("数据不存在,不能删除"));
        try {
            if (!super.removeById(tid)) {
                throw new ApiException("删除失败");
            }
        } finally {
            clearCache(historyModel);
            historyModel.freeData();
        }
    }

    /**
     * 批量删除
     *
     * @param ids 主键集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeBatchIds(List<String> ids) {
        final List<UserTransfer> historyModelList = checkIds(ids);
        try {
            if (!super.removeByIds(ids)) {
                throw new ApiException("批量删除失败");
            }
        } finally {
            clearCaches(historyModelList);
            historyModelList.clear();
        }
    }

    /**
     * 根据传入对象构建查询参数，判断是否存在
     *
     * @param dto 参数
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExists(UserTransferDTO dto) {
        final QueryWrapper<UserTransfer> queryWrapper = createQueryWrapper(dto);
        final boolean exists = super.count(queryWrapper) > 0;
        queryWrapper.clear();
        return exists;
    }

    /**
     * 根据主键判断数据是否存在
     *
     * @param tid 主键
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExistsById(String tid) {
        boolean exists = false;
        if (StringUtils.hasLength(tid)) {
            final UserTransfer model = getById(tid);
            exists = (null != model);
            if (exists) {
                model.freeData();
            }
        }
        return exists;
    }

    /**
     * 根据主键获取数据
     *
     * @param tid 主键
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public UserTransfer getById(String tid) {
        final String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, tid);
        return redisTemplateUtil.get(new CacheKey().setKey(key).setTimeout(ConstantDto.REDIS_TIME_OUT_DAY), () -> super.getById(tid), UserTransfer.class);
    }

    /**
     * 根据主键获取数据
     *
     * @param tid 主键
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public UserTransferDTO getByIdDto(String tid) {
        return Optional.ofNullable(getById(tid)).map(this::modelToDto).orElse(null);
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public UserTransfer getOne(UserTransferDTO dto) {
        final QueryWrapper<UserTransfer> queryWrapper = createQueryWrapper(dto);
        UserTransfer model = super.getOne(queryWrapper);
        queryWrapper.clear();
        return model;
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public UserTransferDTO getOneDto(UserTransferDTO dto) {
        return Optional.ofNullable(getOne(dto)).map(this::modelToDto).orElse(null);
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据
     */
    @Transactional(readOnly = true)
    public List<UserTransfer> getList(UserTransferDTO dto) {
        final QueryWrapper<UserTransfer> queryWrapper = createQueryWrapper(dto);
        final List<UserTransfer> list = super.list(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据，转换成页面对象
     */
    @Transactional(readOnly = true)
    public List<UserTransferDTO> getListDto(UserTransferDTO dto) {
        return modelToDtoList(getList(dto));
    }

    /**
     * 分页数据
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<UserTransferDTO> page(Page<UserTransfer> page, UserTransferDTO dto) {
        final QueryWrapper<UserTransfer> queryWrapper = createQueryWrapper(dto);
        Page<UserTransfer> iPage = baseMapper.selectPage(page, queryWrapper);
        Optional.ofNullable(iPage.getRecords()).ifPresent(this::modelToDtoList);
        queryWrapper.clear();
        return iPage.convert(this::modelToDto);
    }

    /**
     * 待转账列表
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<UserToBeTransferDTO> userToBeTransferPage(Page<UserToBeTransferDTO> page, UserToBeTransferDTO dto) {
        return baseMapper.userToBeTransferPage(page, dto);
    }

    /**
     * 已转账列表
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<UserDividedDTO> dividedPage(Page<UserDividedDTO> page, UserDividedDTO dto) {
        return baseMapper.dividedPage(page, dto);
    }

    /**
     * 待转账详情
     *
     * @param userId 人员ID
     * @return 详情
     */
    @Transactional(readOnly = true)
    public UserToBeTransferDTO userToBeTransferByUserId(String userId) {
        return baseMapper.userToBeTransferByUserId(userId);
    }

    /**
     * 清空缓存
     *
     * @param model 数据库对象
     */
    public void clearCache(UserTransfer model) {
        if (null == model) {
            return;
        }
        redisTemplateUtil.deleteByKey(RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, model.getTid()));
    }

    /**
     * 批量清空缓存
     *
     * @param list 数据库对象
     */
    public void clearCaches(List<UserTransfer> list) {
        if (null == list || list.isEmpty()) {
            return;
        }
        List<String> ids = list.stream().map(model -> RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, model.getTid())).collect(toList());
        if (!ids.isEmpty()) {
            redisTemplateUtil.deleteByKey(ids);
            ids.clear();
        }
    }

    /**
     * 数据库对象转页面对象
     *
     * @param model 数据库对象
     * @return 页面对象
     */
    public UserTransferDTO modelToDto(UserTransfer model) {
        final UserTransferDTO dto = new UserTransferDTO();
        dto.setTid(model.getTid());
        dto.setUserName(model.getUserName());
        dto.setCardNumber(model.getCardNumber());
        dto.setOpenBank(model.getOpenBank());
        dto.setTransferAmount(model.getTransferAmount());
        dto.setAddTime(model.getAddTime());
        dto.setUpdateTime(model.getUpdateTime());
        dto.setUserId(model.getUserId());
        dto.setOperator(model.getOperator());
        dto.setOperateOrgId(model.getOperateOrgId());
        buildDisplay(dto);
        return dto;
    }

    /**
     * 页面对象转数据库对象
     *
     * @param dto 页面对象
     * @return 数据库对象
     */
    public UserTransfer dtoToModel(UserTransferDTO dto) {
        if (null == dto) {
            return null;
        }
        final UserTransfer model = new UserTransfer();
        model.setTid(dto.getTid());
        model.setUserName(dto.getUserName());
        model.setCardNumber(dto.getCardNumber());
        model.setOpenBank(dto.getOpenBank());
        model.setTransferAmount(dto.getTransferAmount());
        model.setAddTime(dto.getAddTime());
        model.setUpdateTime(dto.getUpdateTime());
        model.setUserId(dto.getUserId());
        model.setOperator(dto.getOperator());
        model.setOperateOrgId(dto.getOperateOrgId());
        return model;
    }

    /**
     * 码表转换
     *
     * @param dto 页面对象
     */
    public void buildDisplay(UserTransferDTO dto) {
        dto.setTransferAmountDisplay(dto.getTransferAmount().stripTrailingZeros().toPlainString());
        dto.setOperatorDisplay(SpringContextHolder.getBean(LoginUserService.class).idToDisplay(dto.getOperator()));
        dto.setOperateOrgIdDisplay(SpringContextHolder.getBean(OrgService.class).idToDisplay(dto.getOperateOrgId()));
        Optional.ofNullable(SpringContextHolder.getBean(BaseUserInfoService.class).getById(dto.getUserId())).ifPresent(b -> {
            dto.setMobile(b.getMobile());
        });
    }

    /**
     * 数据库对象转页面对象
     *
     * @param list 数据库对象
     * @return 页面对象
     */
    public List<UserTransferDTO> modelToDtoList(List<UserTransfer> list) {
        return Optional.ofNullable(list).orElse(Collections.emptyList()).stream().map(this::modelToDto).collect(toList());
    }

    /**
     * 页面对象转数据库对象
     *
     * @param list 页面对象
     * @return 数据库对象
     */
    public List<UserTransfer> dtoToModelList(List<UserTransferDTO> list) {
        return Optional.ofNullable(list).orElse(Collections.emptyList()).stream().map(this::dtoToModel).collect(toList());
    }

    /**
     * 拼装查询参数，除了主键的其他字段会自动加入进来
     *
     * @param dto 查询参数，不为空的属性会被自动加入
     */
    public QueryWrapper<UserTransfer> createQueryWrapper(UserTransferDTO dto) {
        final QueryWrapper<UserTransfer> queryWrapper = new QueryWrapper<>();
        if (null != dto) {
            if (StringUtils.hasLength(dto.getUserId())) {
                queryWrapper.eq("user_id", dto.getUserId());
            }
            if (StringUtils.hasLength(dto.getUserName())) {
                queryWrapper.eq("user_name", dto.getUserName());
            }
            if (StringUtils.hasLength(dto.getCardNumber())) {
                queryWrapper.eq("card_number", dto.getCardNumber());
            }
            if (StringUtils.hasLength(dto.getOpenBank())) {
                queryWrapper.eq("open_bank", dto.getOpenBank());
            }
            if (StringUtils.hasLength(dto.getUserId())) {
                queryWrapper.eq("user_id", dto.getUserId());
            }
            if (StringUtils.hasLength(dto.getOperator())) {
                queryWrapper.eq("operator", dto.getOperator());
            }
            if (StringUtils.hasLength(dto.getOperateOrgId())) {
                queryWrapper.eq("operate_org_id", dto.getOperateOrgId());
            }
        }
        queryWrapper.orderByDesc("add_time");
        return queryWrapper;
    }

    /**
     * 批量检查主键在数据库中是否存在
     *
     * @param ids 主键集合
     * @return 主键查询出来的集合
     */
    public List<UserTransfer> checkIds(List<String> ids) {
        if (null == ids || ids.isEmpty()) {
            throw new ApiException("ids不能为空");
        }
        final List<UserTransfer> list = super.listByIds(ids);
        if (list.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(ids.toArray()));
        }
        if (ids.size() == list.size()) {
            return list;
        }
        List<String> dbIds = list.stream().map(UserTransfer::getTid).collect(toList());
        final List<String> notExistsList = ids.stream().filter(id -> !dbIds.contains(id)).collect(toList());
        dbIds.clear();
        if (!notExistsList.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(notExistsList.toArray()));
        }
        return list;
    }

    @Transactional(readOnly = true)
    public void validator(UserTransferDTO dto) {
        UserBank userBank = Optional.ofNullable(userBankService.getById(dto.getUserId())).orElseThrow(() -> new ApiException("用户ID不存在"));
        if (!dto.getUserName().equals(userBank.getUserName())) {
            throw new ApiException("收款户名不匹配");
        }
        if (!dto.getCardNumber().equals(userBank.getCardNumber())) {
            throw new ApiException("收款卡号不匹配");
        }
        if (!dto.getOpenBank().equals(userBank.getOpenBank())) {
            throw new ApiException("开户银行不匹配");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveTransfer(List<TransferDTO.Transfer> list) {
        LocalDateTime now = LocalDateTime.now();
        List<UserTransfer> transferList = new ArrayList<>(list.size());
        List<UserTransferRelation> transferRelationList = new ArrayList<>();
        List<ParkSpace> parkSpaceAllList = new ArrayList<>();
        String operator = SecurityUtils.getLoginUser().getUserId();
        String orgId = SecurityUtils.getLoginUser().getCurrentOrgId();
        for (TransferDTO.Transfer transferDTO : list) {
            List<ParkSpace> parkSpaceList = getParkSpaceList(transferDTO.getUserId());
            String tid = IdWorker.getIdStr();
            buildTransfer(transferDTO, tid, now, operator, orgId, transferList);
            buildRelation(transferDTO, parkSpaceList, tid, now, transferRelationList);
            parkSpaceAllList.addAll(parkSpaceList);
        }
        if (!transferList.isEmpty()) {
            saveBatch(transferList);
            transferList.clear();
        }
        if (!transferRelationList.isEmpty()) {
            userTransferRelationService.saveBatch(transferRelationList);
            transferRelationList.clear();
        }
        if (!parkSpaceAllList.isEmpty()) {
            parkSpaceAllList.forEach(parkSpace -> {
                parkSpace.setUpdateTime(now);
                parkSpace.setDivideAccounts(ConstantDto.SF1);
            });
            parkSpaceService.updateBatchIds(parkSpaceAllList);
            transferRelationList.clear();
        }
    }

    private void buildTransfer(TransferDTO.Transfer transferDTO, String tid, LocalDateTime now, String operator, String orgId, List<UserTransfer> transferList) {
        UserBank userBank = Optional.ofNullable(userBankService.getById(transferDTO.getUserId())).orElseThrow(() -> new ApiException("没有银行卡信息[" + transferDTO.getUserId() + "]"));
        UserTransfer userTransfer = new UserTransfer();
        userTransfer.setTid(tid);
        userTransfer.setUserName(userBank.getUserName());
        userTransfer.setCardNumber(userBank.getCardNumber());
        userTransfer.setOpenBank(userBank.getOpenBank());
        userTransfer.setTransferAmount(new Money(transferDTO.getTransferAmount()).getAmount());
        userTransfer.setAddTime(now);
        userTransfer.setUserId(transferDTO.getUserId());
        userTransfer.setOperator(operator);
        userTransfer.setOperateOrgId(orgId);
        transferList.add(userTransfer);
    }

    private void buildRelation(TransferDTO.Transfer transferDTO, List<ParkSpace> userList, String tid, LocalDateTime now, List<UserTransferRelation> transferRelationListList) {
        AtomicReference<Money> moneyAtomicReference = new AtomicReference<>();
        moneyAtomicReference.set(new Money(0));
        List<UserTransferRelation> relationList = userList.stream().map(u -> {
            System.out.println("u.getDeductionTotalRevenue() = " + u.getDeductionTotalRevenue());
            moneyAtomicReference.set(moneyAtomicReference.get().add(new Money(u.getDeductionTotalRevenue())));
            UserTransferRelation userTransferRelation = new UserTransferRelation();
            userTransferRelation.setTid(tid);
            userTransferRelation.setSid(u.getSid());
            userTransferRelation.setUserId(transferDTO.getUserId());
            userTransferRelation.setAmount(u.getDeductionTotalRevenue());
            userTransferRelation.setAddTime(now);
            return userTransferRelation;
        }).collect(toList());
        Money money = moneyAtomicReference.get();
        if(money.getAmount().compareTo(BigDecimal.ZERO)<=BigDecimal.ZERO.intValue()){
            return;
        }
        Money reqMoney = new Money(transferDTO.getTransferAmount());
        if (!reqMoney.equals(money)) {
            throw new ApiException("金额发生变化,请刷新后提交:[" + transferDTO.getUserId() + "]");
        }
        if(!relationList.isEmpty()){
            transferRelationListList.addAll(relationList);
        }
    }

    /**
     * 查询待分账列表
     *
     * @param userId 查询参数
     * @return 列表
     */
    private List<ParkSpace> getParkSpaceList(String userId) {
        QueryWrapper<ParkSpace> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("publisher",userId);
        queryWrapper.eq("end",ConstantDto.SF1);
        queryWrapper.eq("divide_accounts",ConstantDto.SF0);
        queryWrapper.gt("deduction_total_revenue",0);
        List<ParkSpace> userList = parkSpaceService.getBaseMapper().selectList(queryWrapper);
        if (userList.isEmpty()) {
            throw new ApiException("数据异常,没有分账信息[" + userId + "]");
        }
        queryWrapper.clear();
        return userList;
    }

    @Transactional(readOnly = true)
    public void toBeTransferExport(UserToBeTransferDTO query) {
        final ExportPage<UserToBeTransferDTO, UserToBeTransferDTO> exportPage = new ExportPage<>();
        try {
            final List<UserToBeTransferDTO> list = exportPage.getAllForPage(page -> userToBeTransferPage(page, query).getRecords());
            try {
                ExcelExport.downloadExcel(response, ProductOrderDTO.class, () -> list);
            } catch (IOException e) {
                log.error("导出失败", e);
                throw new ApiException("导出失败");
            }
        } finally {
            exportPage.freeData();
        }
    }

    @Transactional(readOnly = true)
    public void dividedExport(UserDividedDTO query) {
        final ExportPage<UserDividedDTO, UserDividedDTO> exportPage = new ExportPage<>();
        try {
            final List<UserDividedDTO> list = exportPage.getAllForPage(page -> dividedPage(page, query).getRecords());
            try {
                ExcelExport.downloadExcel(response, UserDividedDTO.class, () -> list);
            } catch (IOException e) {
                log.error("导出失败", e);
                throw new ApiException("导出失败");
            }
        } finally {
            exportPage.freeData();
        }
    }
}